#define _BSD_SOURCE 1
#define _XOPEN_SOURCE_EXTENDED 1
#include "ThreadPool.h"
#include "Constants.h"
#include "Filter.h"
#include "Queue.h"
#include "Connection.h"
#include "RequestHeader.h"
#include "RequestParameter.h"
#include "ResponseHeader.h"
#include "Connection.h"
#include "Queue.h"
#include "Global.h"
#include "FileManager.h"
#include "CommandToken.h"
#include "PublicUtils.h"

int initPool(pool_t *pool){
	// 初始化信号量,初值为0
	zem_init(&pool->getfile, 0);
	zem_init(&pool->putfile, 0);

	pthread_create(&pool->putFileThreadt, NULL, putFileThread, (void *)pool);
	pthread_create(&pool->getFileThreadt, NULL, getFileThread, (void *)pool);

    pool->exitFlag = 0;
    return 0;
}


int destoryPool(pool_t *pool){
	pthread_join(pool->getFileThreadt, NULL);
	pthread_join(pool->putFileThreadt, NULL);

	zem_destory(&pool->getfile);
	zem_destory(&pool->putfile);
	

	return 0;
}


/**
 * 请求头信息：
 * putfile
 * filesize = stat
 * offset = -1
 * argcnt = 4
 * 
 * 请求参数信息：
 * token    无锁
 * filemd5  
 * currentDirectory 需要加锁
 * filename
 * 
 * 通信流程:
 * send header
 * recv 1/0
 * sendfile
 * 
*/
void *putFileThread(void* arg){
	pool_t *pool = (pool_t*)arg;

    while(1){
        // P
        zem_wait(&pool->putfile);	

        // 建立socket连接
        int net_fd;
        SocketDriver(&net_fd);

        /* 读取文件 */
        // TODO 加锁
        char fileName[BUFF_SIZE];
        strncpy(fileName, parameter, BUFF_SIZE - 1);

        char currentDir[BUFF_SIZE];
        strncpy(currentDir, currentDirectory, BUFF_SIZE - 1);
        // TODO 解锁

        int fileDescripter = open(fileName, O_RDWR);
        if(fileDescripter == -1){
            printf("no such file\n");
            return (void*)NULL;
        }

        /* 计算文件md5 */
        unsigned char filedmd5[MD5_DIGEST_LENGTH];
        computemd5(fileDescripter, filedmd5);
        char md52string[MD5_DIGEST_LENGTH * 2 + 1];
        /* md5值需要转换成字符串类型 */
        ucharToHexString(filedmd5, md52string, MD5_DIGEST_LENGTH);

        /* 计算完md5值，移动了文件偏移量需要把偏移量移动到头 */
        lseek(fileDescripter, 0, SEEK_SET);

        /* 获得文件大小 */
        long fileSize = getFileSize(fileDescripter);

        /* 发送请求头 */
        RequestHeader *header = createRequestHeader(PUT_FILE, fileSize, -1, 4);
        send(net_fd, header, sizeof(RequestHeader), MSG_NOSIGNAL);
        freeRequestHeader(header);

        /* 发送参数链表 */
        RequestParameter *param = createLinkedList(4, token, md52string, currentDir, fileName);
        sendLinkedList(net_fd, param);
        freeLinkedList(param);

        /* 接收服务器返回响应头 */
        ResponseHeader *response = createResponseHeader(-1, "");
        recvResponseHeader(net_fd, response);
        
        if(response->flag == 0 || response->flag == 1){
            // token过期或文件已存在，那么不需要传文件了
            printf(response->data);
            freeResponseHeader(response);
            return NULL;
        }
        printf(response->data);

        /* 发送文件 */
        sendfile(net_fd, fileDescripter, NULL, fileSize);
        
        close(net_fd);
        close(fileDescripter);
        freeResponseHeader(response);
	
    }
	return NULL;
}


/**
 * 请求头信息：
 * getfile
 * filesize = stat
 * offset = stat
 * argcnt = 3
 * 
 * 请求参数信息：
 * token 无锁
 * filename
 * currentDirectory 需要加锁
 * 
 * 
 * @bug 需要修改，总感觉哪里有问题
 * 
 * 
*/
void *getFileThread(void* arg){
	pool_t *pool = (pool_t*)arg;
    while(1){
        // P
        zem_wait(&pool->getfile);

        /**
         * 判断本地有无此文件
         *      有：获取偏移量
         *      无：偏移量 set 0
         * 
         * 发送请求头：getfile, fileSize(表示本地无此文件), offset, argcnt = 3
         * 发送请求参数：token, filename, current directory
         * 
         * 阻塞等待响应头：
         *      0：token过期，
         *      -1：远程仓库无此文件
         *      1：可下载，准备接收(需要计算偏移量),分两种：文件残缺(无需)，无此文件(需要文件大小)
         *      TODO 还需要返回文件的md5值以便校验文件完整性
        */

        // TODO 加锁
        char fileName[BUFF_SIZE];
        strncpy(fileName, parameter, BUFF_SIZE - 1);

        char currentDir[BUFF_SIZE];
        strncpy(currentDir, currentDirectory, BUFF_SIZE - 1);        
        // TODO 解锁

        off_t offset = 0;
        off_t fileSize = 0;
        int fileDescriptor;
        // 如果已存在该文件，则获取该文件真实写入了多少字节
        if(access(fileName, F_OK) == 0){
            fileDescriptor = open(fileName, O_RDWR);
            offset = getActualWrittenSize(fileName);
            fileSize = getFileSize(fileDescriptor);
        }

        // 建立socket连接
        int net_fd;
        SocketDriver(&net_fd);

        /* 发送请求头 */
        RequestHeader *header = createRequestHeader(GET_FILE, -1, offset, 3);
        send(net_fd, header, sizeof(RequestHeader), MSG_NOSIGNAL);
        freeRequestHeader(header);

        /* 发送参数链表 */
        RequestParameter *param = createLinkedList(3, token, fileName, currentDir);
        sendLinkedList(net_fd, param);
        freeLinkedList(param);

        /* 阻塞接收响应头 */
        ResponseHeader *response = createResponseHeader(-1, "");
        recvResponseHeader(net_fd, response);

        if(response->flag == 0 || response->flag == -1){
            /* token过期，或远程无此文件 */
            printf("%s\n", response->data);
            return NULL;
        }

        if(fileSize == 0){
            // 若不存在则先创建,创建文件要放在接收响应头之后
            fileDescriptor = open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
            ftruncate(fileDescriptor, atol(response->data));
        }

        /* 设置偏移量 */
        lseek(fileDescriptor, offset, SEEK_SET);
        void *p = mmap(NULL, atol(response->data), PROT_READ | PROT_WRITE, MAP_SHARED, fileDescriptor, offset);
        /* 此处写入长度为总长度减去偏移量 */
        recvn(net_fd, p, atol(response->data) - offset);
        munmap(p, atol(response->data));


        // TODO 需要校验文件完整性
        freeResponseHeader(response);
        close(fileDescriptor);
        close(net_fd);
    }
	return NULL;
}
